package cn.eric.framework.auth.utils;

import cn.eric.framework.auth.dao.AuthorityDao;
import cn.eric.framework.auth.dao.RoleDao;
import cn.eric.framework.auth.dao.UserDao;
import cn.eric.framework.auth.domain.*;
import cn.eric.framework.auth.utils.auth.AuthUtil;
import cn.eric.framework.log.log4j2.Log4Framework;
import cn.eric.framework.utils.CfgUtil;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
import org.xml.sax.SAXException;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.xml.bind.JAXBException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;

@Component
public class EAuthUtil {
    private static final String LOG_TITLE = "EAuth";

    @Resource
    private AuthUtil authUtil;

    @Resource
    private String headerKeyUser;

    @Resource
    private UserDao userDao;
    @Resource
    private AuthorityDao authorityDao;
    @Resource
    private RoleDao roleDao;
    @Resource
    private Logger logger;

    /**
     * 初始化权限模块
     *
     * @param path 配置文件路径
     */
    public void initEAuth(String path) throws IOException, IllegalAccessException, JAXBException, InstantiationException {
        // to check is cfg correct
        logger.info(Log4Framework.create(LOG_TITLE, "开始验证配置文件正确性。"));
        try {
            if (CfgUtil.validateXMLWithSchema(EAuthConstant.EAUTH_CFG_XSD_PATH, path)) {
                logger.info(Log4Framework.create(LOG_TITLE, "配置文件正确。"));

                logger.info(Log4Framework.create(LOG_TITLE, "解析权限列表。"));
                // 处理 Authority
                EAuthCfg eAuthCfg = CfgUtil.JAXBParser(EAuthCfg.class, path);
                // 把单独的权限id转换成级联的id
                for (Authority authority : eAuthCfg.getAuthorities()) {
                    convertAuthorityId2CreateTree(authority, null);
                }
                // save authorities
                for (Authority authority : eAuthCfg.getAuthorities()) {
                    authorityDao.save(authority);
                }
                logger.info(Log4Framework.create(LOG_TITLE, "解析权限列表完成。"));

                // 处理 Role
                for (Role role : eAuthCfg.getRoles()) {
                    roleDao.save(role);
                    logger.info(Log4Framework.create(LOG_TITLE, role.toString()));
                }

                // 处理 Rule
                logger.info(Log4Framework.create(LOG_TITLE, "解析规则。"));
                EAuthRuleMap.init(eAuthCfg.getRules());
                for (Rule rule : eAuthCfg.getRules()) {
                    logger.info(Log4Framework.create(LOG_TITLE, rule.toString()));
                }
                logger.info(Log4Framework.create(LOG_TITLE, "解析规则完成。"));
            }
        } catch (SAXException | URISyntaxException e) {
            logger.fatal(Log4Framework.create(LOG_TITLE, "配置文件验证失败。", e.getMessage()));
        }
    }

    /**
     * 登陆
     *
     * @param uid     用户Uid
     * @param session session
     */
    public void login(String uid, HttpSession session, HttpServletRequest request) {
        authUtil.login(uid, session, request);
    }

    /**
     * 工具方法 获得User对象
     *
     * @param session session
     * @return 用户的User
     */
    public User getUser(HttpServletRequest request, HttpSession session) {
        String uid = authUtil.getUid(session, request);
        if (uid == null) {
            return null;
        }
        return userDao.get(uid);
    }

    /**
     * 注册 为默认注册提供的方法 最终方法
     *
     * @param roleIds
     * @return
     */
    public User register(List<String> roleIds) {
        ArrayList<Role> roles = new ArrayList<>();
        for (String roleId : roleIds) {
            roles.add(roleDao.getRole(roleId));
        }
        User user = new User();
        HashSet<String> authorityIds = new HashSet<>();
        for (Role r : roles) {
            authorityIds.addAll(r.getAuthorityIds());
        }
        user.setRoleIds(roleIds);
        user.setAuthorityIds(new ArrayList<>(authorityIds));

        userDao.save(user);

        return user;
    }

    /**
     * 注册 通过权限list注册用户
     *
     * @param authorityIds 权限列表
     * @return 生成的User对象
     */
    public User registerByAuthorities(List<String> authorityIds) {
        User user = new User();
        HashSet<String> authorityIdsSet = new HashSet<>(authorityIds);
        user.setAuthorityIds(new ArrayList<>(authorityIdsSet));
        userDao.save(user);

        return user;
    }

    /**
     * 注册 便于开发
     *
     * @param roleIds 用户的角色Id
     * @return 一个完整的User对象
     */
    public User register(String... roleIds) {
        ArrayList<String> roleIdList = new ArrayList<>();
        Collections.addAll(roleIdList, roleIds);
        return register(roleIdList);
    }

    public void overwriteAuthority(String uid, List<String> authorityIds) {
        User user = userDao.get(uid);
        if (user != null) {
            HashSet<String> authorityIdsSet = new HashSet<>(authorityIds);
            user.setAuthorityIds(new ArrayList<>(authorityIdsSet));
        }
    }

    /**
     * 用于判断是否符合权限
     *
     * @param had    拥有的权限
     * @param target 需要的权限
     * @return
     */
    public boolean isAuthorityEnough(List<String> had, List<String> target) {
        for (String t : target) {
            if (had.contains(t)) {
                return true;
            } else {
                for (String h : had) {
                    String[] hSplit = h.split(EAuthConstant.EAUTH_AUTHORITY_SPLIT);
                    String[] tSplit = t.split(EAuthConstant.EAUTH_AUTHORITY_SPLIT);
                    if (hSplit.length < tSplit.length) {
                        boolean isEnough = true;
                        for (String aHSplit : hSplit) {
                            if (!aHSplit.equals(tSplit.length)) {
                                isEnough = false;
                                break;
                            }
                        }
                        if (isEnough) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    private void convertAuthorityId2CreateTree(Authority authority, Authority parent) {
        if (parent != null) {
            String idTemplate = "%s" + EAuthConstant.EAUTH_AUTHORITY_SPLIT + "%s";
            authority.setId(String.format(idTemplate, parent.getId(), authority.getId()));
            authority.setParent(parent);
        }
        for (Authority a : authority.getChildAuthorities()) {
            convertAuthorityId2CreateTree(a, authority);
        }
    }
}
